package com.hmdp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import com.hmdp.utils.RedisConstants;
import com.hmdp.utils.RegexUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Slf4j
@Service
public class  UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Override
    public Result sendCode(String phone, HttpSession session) {
        // 校验 手机 号码, 一般 就是 正则 表达式 前后端 都要校验 防止 直接 绕过前端 向后端 发请求

        if(RegexUtils.isPhoneInvalid(phone)){  // 这个 代表的 是 如果 手机号 无效 那么 就 返回 true
            return Result.fail("手机号格式不匹配");
        }


        // 失败 就返回 错误 信息

        // 成功 生成 对应的 验证码

//        String code  = RandomUtil.randomNumbers(6);  // 生成 随机数字
        String code = "123456";  // fan 临时 固定 验证码


        //  使用redis 来 代替session
        // 设置 key 的 有效期， 就像 验证码的 有效期 一样 这样 过了 时间 redis里面 没有 这个 key了 登录的 时候 验证 code 就 会失败，这样就
        stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY+phone,code,2, TimeUnit.MINUTES);  // 设置 两分钟

        // 保存到 session 域里面 本次 会话 都是用 这个
        // 每一个 会话 都会 有 一个session 对象，所以 每一个 用户 只会 有一个 code
//        session.setAttribute("code",code);  // 使用 phone 作为 key 这样 不会 出现 覆盖
//        System.out.println("zhang");
        // 模拟 发送 验证码
        log.info("发送短信验证码成功，验证码："+code);
        return Result.ok(code);
    }


    // 肯定要做登录 验证  之前 发送了
    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        // 完成 登录 如果 没有 改 用户 还需要 注册
        if(RegexUtils.isPhoneInvalid(loginForm.getPhone())){  // 这个 代表的 是 如果 手机号 无效 那么 就 返回 true
            return Result.fail("手机号格式不匹配");
        }
        // 校验 验证码  因为 之前
        String cachecode = stringRedisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY+loginForm.getPhone());  // 获取 redis 里卖弄的 code 如果 没 过期 就可以 获取到
        //先 不 检查 这个
//        if(cachecode==null || !loginForm.getCode().equals(cachecode)){  // 保证 没有
//            return Result.fail("验证码不正确");
//        }
        // 校验 验证码  因为 之前 记录了

        // 然后 就 要 查询 改用户 存在 与否 使用mp
        String phone = loginForm.getPhone();
        User user = query().eq("phone", phone).one();  // 查 某个 手机号 对应的 人 有没有

//        // mp 使用 对了 版本问题 3.5 以上 才行 不然 找不到 东西 会报错
//        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
//        wrapper.eq(User::getPhone,phone);
//        userMapper.selectOne(wrapper);
        // 登录 的 时候 是 不需要 拦截的
        if(user ==null){
            user = CreteUser(loginForm.getPhone());
        }

        // 首先 现在 有了用户 信息 然后 将 redis 替换 session session 有 key - balue  所以 要 有 yoken

        // token 创建
        String token = UUID.randomUUID().toString(true);
        // 用户 对象 转变为 map

        UserDTO userDTO = BeanUtil.copyProperties(user,UserDTO.class);  // 表示 建立 连接 的

        Map<String, Object> stringObjectMap = BeanUtil.beanToMap(userDTO);  // 转换 之后 key 都是 string 类型的即 字段名称  value 和 字段的类型 相关 现在 使用的 是 stringre... 要求 所有的 key value 值 都是 string  所以 要将map 里面的 id 的 值 变成 string
        //
        stringObjectMap.replace("id",stringObjectMap.get("id").toString());  // 这个 因为 使用的 是StrinRedisTemplate 所以 要求 所有的 key value 值 都是 string  所以 要将map 里面的 id 的 值 变成 string

        String key = RedisConstants.LOGIN_USER_KEY+token;  //token 就是 sessionID的作用
        // 保存 到redis
        stringRedisTemplate.opsForHash().putAll(key,stringObjectMap);
        // 设置 token 的 有效期(这个 不能 动态， 和 session的 过期的 意思 不一样 所以 要 在 一个 地方 动态的 改变 key的 有效期)
        // redis 是 内存 数据库 ，所以 对于 suoyou de key 能设置 有效期 最好

        // 通用命令 设置 key 的 有效期
        stringRedisTemplate.expire(key,RedisConstants.LOGIN_USER_TTL,TimeUnit.SECONDS);


        //session.setAttribute("user", BeanUtil.copyProperties(user,UserDTO.class));

        // 返回 token 给 前端， 这样 之后的 访问 都要 携带这个

        return Result.ok(token);  //

    }

    private User CreteUser(String pp) {
        User user = new User();
        user.setPhone(pp);
        user.setNickName("user_"+RandomUtil.randomNumbers(10));  //这里 不同的 手机 账号 就 是 不同的 用户
        save(user);  //  插入一个 新的 用户
        return user;
    }
}
